
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>K8S 最佳实践-映射外部服务 Endpoints  ExternalName</title>
            </head>
            <body>
            <a href="https://andyoung.blog.csdn.net">原作者博客</a>
            <div id="content_views" class="markdown_views prism-atom-one-light">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <p>大多数 Kubernetes 用户都有可能用到集群外部的服务。例如，您可能使用 Twillio API 发送短信，或使用 Google Cloud Vision API 进行图像分析。</p> 
<p>如果位于不同环境中的应用连接相同的外部端点，并且您不打算将外部服务引入 Kubernetes 集群，那么在代码中直接使用外部服务端点是完全可以的。然而，很多时候情况并非如此。</p> 
<p>数据库就是一个很好的例子。虽然一些云原生数据库（如 Cloud Firestore 或 Cloud Spanner）对所有访问均使用一个端点，但大多数数据库对不同实例都有单独的端点。</p> 
<p>说到这里，您可能会认为，就查找端点而言，ConfigMap 是个不错的解决方案。只需将端点地址存储在 ConfigMap 中，并将其作为环境变量用于代码中。此解决方案的确有效，但也存在一些缺点。您需要修改部署以包含 ConfigMap 并编写额外的代码以从环境变量中读取。但最重要的是，如果端点地址发生变化，您可能需要重启所有正在运行的容器以获取更新后的端点地址。</p> 
<p>在本集的 “Kubernetes 最佳实践” 中，我们会学习如何将 Kubernetes 内置服务发现机制运用于集群外部运行的服务，像使用集群内的服务一样使用外部服务！通过这种方式，您可以在开发环境和生产环境中实现相同的功能，如果您最终将服务移入集群内，则不需要更改任何代码。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/0e0a531d6afd7172c4a66f99c1e6f83f.png" alt="img"></p> 
<h3><a id="_1_IP__12"></a><strong>场景 1：具有 IP 地址的集群外数据库</strong></h3> 
<p>其中一个常见场景是在集群外部托管自己的数据库，例如在 Google 计算引擎实例中。如果您在 Kubernetes 内部和外部分别运行一些服务，或者需要在 Kubernetes 允许的基础上获得更多定制或控制，通常可采用上述这种方式。</p> 
<p>希望未来某个时候您可以将所有服务都移入集群内，但在此之前将是“内外混用”的状态。幸运的是，您可以使用静态 Kubernetes 服务来缓解上述痛点。</p> 
<p>在本例中，我使用 Cloud Launcher 创建了一个 MongoDB 服务器。由于此服务器在与 Kubernetes 集群相同的网络（或 VPC）中创建，因此可以使用高性能的内部 IP 地址访问。在 Google Cloud 中，这是默认设置，因此无需进行任何特殊配置。</p> 
<p>现在我们有了 IP 地址，那么第一步就是创建服务：</p> 
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: mongo
spec:
  type: ClusterIP
  ports:
  - port: 27017  # Service端口       
    targetPort: 27017 # pod端口
</code></pre> 
<p>您可能会注意到此服务没有 Pod 选择器。此操作将创建一个服务，但它不知道往哪里发送流量。这样一来，您可以手动创建一个将从此服务接收流量的 Endpoints 对象。</p> 
<pre><code>kind: Endpoints
apiVersion: v1
metadata:
  name: mongo
subsets:
  - addresses:
      - ip: 192.168.11.13
    ports:
      - port: 9200
</code></pre> 
<p>您可以看到 Endpoints 手动定义了数据库的 IP 地址，并且使用的名称与服务名称相同。Kubernetes 将 Endpoints 中定义的所有 IP 地址视为与常规 Kubernetes Pod 一样。现在您可以用一个简单的连接字符串访问数据库：</p> 
<blockquote> 
 <p>mongodb://mongo</p> 
</blockquote> 
<p>&gt; 根本不需要在代码中使用 IP 地址！如果以后 IP 地址发生变化，您可以为端点更新 IP 地址，而应用无需进行任何更改。</p> 
<h3><a id="_2_URI__58"></a><strong>场景 2：具有 URI 的远程托管数据库</strong></h3> 
<p>如果您使用的是来自第三方的托管数据库服务，它们可能会为您提供可用于连接的统一资源标识符 (URI)。如果它们为您提供 IP 地址，则可以使用场景 1 中的方法。</p> 
<p>在本例中，我在 mLab 上托管了两个 MongoDB 数据库。一个是我的开发数据库，另一个是生产数据库。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/2e5f54a127405f7ff934b9cf497c772e.png" alt="img"></p> 
<p>这些数据库的连接字符串如下所示：</p> 
<blockquote> 
 <p>mongodb://:@ds149763.mlab.com:49763/devmongodb://:@ds145868.mlab.com:45868/prodmLab</p> 
</blockquote> 
<p>为您提供了动态 URI 和动态端口，您可以看到两者都不同。我们来使用 Kubernetes 基于这些差异创建一个抽象层。在本例中，我们将连接开发数据库。</p> 
<p>您可以创建一个 “ExternalName” Kubernetes 服务，此服务为您提供将流量重定向到外部服务的静态 Kubernetes 服务。此服务在内核级别执行简单的 CNAME 重定向，因此对性能的影响非常小。</p> 
<p>服务的 YAML 如下所示：</p> 
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: mongo
spec:
  type: ExternalName 
  externalName: ds149763.mlab.com  
</code></pre> 
<p>现在，您可以使用更简化的连接字符串：</p> 
<blockquote> 
 <p><code>mongodb://&lt;dbuser&gt;:&lt;dbpassword&gt;@mongo:&lt;port&gt;/dev</code></p> 
</blockquote> 
<p>由于 “ExternalName” 使用 CNAME 重定向，因此无法执行端口重映射。对于使用静态端口的服务来说，这可能不成问题，然而本例中使用的是动态端口。mLab 免费版为您提供了动态端口号，并且不允许更改。这意味着您需要对开发和生产数据库使用其他连接字符串。</p> 
<p>但如果您可以获取 IP 地址，就可以执行端口重映射，关于此内容，我将在下一部分进行介绍。</p> 
<h4><a id="_3_URI__96"></a><strong>场景 3：具有 URI 和端口重映射功能的远程托管数据库</strong></h4> 
<p>CNAME 重定向对于每个环境均使用相同端口的服务非常有效，但如果每个环境的不同端点使用不同的端口，CNAME 重定向就略显不足。幸运的是我们可以使用一些基本工具来解决这个问题。</p> 
<ul><li>第一步是从 URI 获取 IP 地址。</li></ul> 
<p>对 URI 运行 nslookup、hostname 或 ping 命令即可获取数据库的 IP 地址。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/cc952991bf12e9d070cdeb6ec3711f8e.jpeg" alt="img"></p> 
<p>您现在可以创建一个重新映射 mLab 端口的服务，并为此 IP 地址创建端点。</p> 
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: mongo
spec:
  type: ClusterIP
  ports:
  - port: 27017  # Service端口       
    targetPort: 27017 # pod端口
---
kind: Endpoints
apiVersion: v1
metadata:
  name: mongo
subsets:
  - addresses:
      - ip: 192.168.11.13
    ports:
      - port: 9200
</code></pre> 
<p><strong>注：URI 可以使用 DNS 在多个 IP 地址之间进行负载平衡，因此，如果 IP 地址发生变化，这个方法可能会有风险！如果您通过上述命令获取多个 IP 地址，则可以将所有这些地址都包含在 Endpoints YAML 中，并且 Kubernetes 会在所有 IP 地址之间进行流量的负载平衡。</strong></p> 
<p>通过这种方式，您无需指定端口即可连接到远程数据库。Kubernetes 服务重映射端口的过程完全透明！</p> 
<blockquote> 
 <p>mongodb://:@mongo/dev</p> 
</blockquote> 
<h2><a id="_138"></a><strong>结论</strong></h2> 
<p>将外部服务映射到内部服务可让您未来灵活地将这些服务纳入集群，同时最大限度地减少重构工作。即使您今天不打算将服务加入集群，以后可能也会这样做！而且，这样一来，您可以更轻松地管理和了解组织所使用的外部服务。</p> 
<p>如果外部服务具有有效域名，并且您不需要重新映射端口，那么使用 “ExternalName” 服务类型将外部服务映射到内部服务十分简便、快捷。如果您没有域名或需要执行端口重映射，只需将 IP 地址添加到端点并使用即可。</p>
                </div>
            </body>
            </html>
            